import { Component, Element, Host, Method, Prop, Watch, h } from '@stencil/core';

/**
 * 分组面板项
 *
 * @export
 * @class IbzSplitViewPanel
 */
@Component({
    tag: 'ibz-split-view-panel',
    styleUrl: 'ibz-split-view-panel.scss',
})
export class IBzSplitViewPanel {
    /**
     * 组件DOM实例
     *
     * @type {HTMLIbzSplitViewPanelElement}
     * @memberof IBzSplitViewPanel
     */
    @Element()
    el: HTMLIbzSplitViewPanelElement;

    /**
     * 布局容器
     *
     * @type {HTMLIbzSplitViewElement}
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    container!: HTMLIbzSplitViewElement;

    /**
     * 所有面板
     *
     * @type {HTMLIbzSplitViewPanelElement[]}
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    panels: HTMLIbzSplitViewPanelElement[] = [];

    /**
     * 拖拽节点
     *
     * @type {HTMLIbzSplitSashElement}
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    dragTrigger!: HTMLIbzSplitSashElement;

    /**
     * 当前所属位置下标
     *
     * @type {number}
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    index = 0;

    /**
     * 是否可见
     *
     * @type {boolean}
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    visible = true;

    /**
     * 顶部距离
     *
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    top = 0;

    /**
     * 距离顶部旧值
     *
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    oldTop = 0;

    /**
     * 容器高度
     *
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    height = 0;
    oldHeight = 0;

    /**
     * 容器最小高度
     *
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    minHeight = 100;
    oldMinHeight = 100;
    @Watch('minHeight')
    watchMinHeight(): void {
        this.container.calcMinHeight();
    }

    /**
     * 之前面板
     *
     * @type {HTMLIbzSplitViewPanelElement}
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    beforePanel?: HTMLIbzSplitViewPanelElement;

    /**
     * 之后面板
     *
     * @type {HTMLIbzSplitViewPanelElement}
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    afterPanel?: HTMLIbzSplitViewPanelElement;

    /**
     * 是否锁定高度
     *
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    isLockHeight = false;

    /**
     * 是否正在锁定操作中
     *
     * @memberof IBzSplitViewPanel
     */
    isLockingHeight = false;

    /**
     * 是否正在移动中
     *
     * @memberof IBzSplitViewPanel
     */
    isMoving = false;

    /**
     * 是否启用过度动画
     *
     * @memberof IBzSplitViewPanel
     */
    @Prop()
    transition = false;
    @Watch('transition')
    watchTransition(): void {
        if (this.transition) {
            this.el.classList.add('transition');
        } else {
            this.el.classList.remove('transition');
        }
    }

    /**
     * 是否为最后一个
     *
     * @readonly
     * @type {boolean}
     * @memberof IBzSplitViewPanel
     */
    get isLast(): boolean {
        if (this.index === this.panels.length - 1) {
            return true;
        }
        return false;
    }

    /**
     * 是否为第一个
     *
     * @readonly
     * @type {boolean}
     * @memberof IBzSplitViewPanel
     */
    get isFirst(): boolean {
        if (this.index === 0) {
            return true;
        }
        return false;
    }

    /**
     * 获取容器高度
     *
     * @readonly
     * @type {number}
     * @memberof IBzSplitViewPanel
     */
    get containerHeight(): number {
        return this.container.offsetHeight;
    }

    /**
     * 启用过度动画
     *
     * @memberof IBzSplitViewPanel
     */
    enableTransition(): void {
        this.panels.forEach(item => (item.transition = true));
    }

    /**
     * 关闭过度动画
     *
     * @memberof IBzSplitViewPanel
     */
    disableTransition(): void {
        setTimeout(() => {
            this.panels.forEach(item => (item.transition = false));
        }, 300);
    }

    render() {
        return (
            <Host class='ibz-split-view-panel' style={{ top: this.top + 'px', height: this.height + 'px' }}>
                <slot />
            </Host>
        );
    }

    /**
     * 设置高度
     *
     * @param {number} height
     * @memberof IBzSplitViewPanel
     */
    @Method()
    async setHeight(height: number) {
        if (!this.isLockHeight) {
            if (height < this.minHeight) {
                this.height = this.minHeight;
            } else {
                this.height = height;
            }
        } else {
            this.height = this.minHeight;
        }
    }

    /**
     * 拖拽开始
     *
     * @memberof IBzSplitViewPanel
     */
    @Method()
    async draggingStart() {
        this.oldTop = this.top;
    }

    /**
     * 拖拽结束
     *
     * @memberof IBzSplitViewPanel
     */
    @Method()
    async draggingEnd() {
        this.oldTop = this.top;
    }

    /**
     * 移动
     *
     * @param {number} top
     * @memberof IBzSplitViewPanel
     */
    @Method()
    async draggingMove(top: number) {
        const y = top - this.top;
        await this.move(top, y);
    }

    /**
     * 根据当前所移动的位置距离顶部的距离计算移动位置
     *
     * @param {number} top
     * @param {number} y
     * @param {('up' | 'down')} [direction]
     * @memberof IBzSplitViewPanel
     */
    @Method()
    async move(top: number, y: number, direction: 'up' | 'down' = y > 0 ? 'down' : 'up') {
        if (this.isMoving) {
            return;
        }
        this.isMoving = true;
        const minTop = this.getBeforeMinHeight();
        const maxTop = this.containerHeight - (this.getAfterMinHeight() + this.minHeight);
        if (top < minTop) {
            this.top = minTop;
        } else if (top > maxTop) {
            this.top = maxTop;
        } else {
            this.top = top;
        }
        await this.moveOther(y, direction);
        let height = this.containerHeight - this.top;
        if (this.afterPanel) {
            height = this.afterPanel.top - this.top;
        }
        if (this.beforePanel) {
            await this.beforePanel.setHeight(this.top - this.beforePanel.top);
        }
        await this.setHeight(height);
        this.isMoving = false;
    }

    /**
     * 移动其他
     *
     * @param {number} y 移动距离
     * @param {('up' | 'down')} direction 移动方向
     * @memberof IBzSplitViewPanel
     */
    async moveOther(y: number, direction: 'up' | 'down') {
        if (direction === 'up' && this.beforePanel) {
            // 向上移动
            const minTop = this.beforePanel.top + this.beforePanel.minHeight;
            if (minTop > this.top) {
                await this.beforePanel.move(this.beforePanel.top + y, y, 'up');
            }
        } else if (this.afterPanel) {
            // 向下移动
            const maxTop = this.top + this.minHeight;
            if (maxTop > this.afterPanel.top) {
                await this.afterPanel.move(this.afterPanel.top + y, y, 'down');
            }
        }
        if (this.isLockHeight) {
            if (direction === 'up' && this.afterPanel) {
                await this.afterPanel.move(this.afterPanel.top + y, y, 'up');
            } else if (this.beforePanel.index !== 0 && this.beforePanel.isLockHeight) {
                await this.beforePanel.move(this.beforePanel.top + y, y, 'down');
            }
        } else if (this.beforePanel.isLockHeight && direction === 'down') {
            await this.beforePanel.move(this.beforePanel.top + y, y, 'down');
        }
    }

    /**
     * 锁定高度
     *
     * @param {number} height
     * @memberof IBzSplitViewPanel
     */
    @Method()
    async lockHeight(height: number) {
        if (!this.isLockHeight) {
            this.isLockingHeight = true;
            this.enableTransition();
            this.isLockHeight = true;
            // 设置最小高度
            this.oldMinHeight = this.minHeight;
            this.minHeight = height;
            // 设置当前高度
            this.oldHeight = this.height;
            this.height = height;
            if (this.isLast) {
                const lock = this.beforeAllLockHeightAndSelf();
                let top = this.containerHeight - this.height;
                if (lock) {
                    top = this.getBeforeMinHeight() - this.minHeight;
                }
                await this.draggingMove(top);
            } else if (this.isFirst) {
                this.top = 0;
                await this.afterPanel.draggingMove(this.height);
            } else {
                // 上下是否全锁着
                const bLock = this.beforeAllLockHeightAndSelf();
                // 自己+前边全锁着，让后边跟上
                if (bLock) {
                    await this.afterPanel.draggingMove(this.top + this.height);
                } else {
                    const top = this.afterPanel.top - this.height;
                    await this.draggingMove(top);
                }
            }
            this.panels.forEach(item => item.calcIsShowDragTrigger());
            this.container.tick();
            this.disableTransition();
            this.isLockingHeight = false;
        }
    }

    /**
     * 解锁高度限制
     *
     * @param {number} height
     * @memberof IBzSplitViewPanel
     */
    @Method()
    async unlockHeight() {
        if (this.isLockHeight) {
            this.enableTransition();
            this.isLockHeight = false;
            // 还原最小高度
            const oldMinHeight = this.minHeight;
            this.minHeight = this.oldMinHeight;
            this.oldMinHeight = this.minHeight;
            // 还原容器高度
            const height = this.height;
            this.height = this.oldHeight;
            this.oldHeight = height;
            // 上下是否都锁着，上边全锁着推下边的，下边全锁着推上边的
            const bLock = this.beforeAllLockHeight();
            const aLock = this.afterAllLockHeight();
            if (this.isLast) {
                const top = this.containerHeight - this.height;
                await this.draggingMove(top);
            } else if (this.isFirst) {
                this.top = 0;
                if (aLock) {
                    const num = this.getAfterMinHeight();
                    const top = this.containerHeight - num;
                    await this.afterPanel.draggingMove(top);
                } else {
                    await this.afterPanel.draggingMove(this.height);
                }
            } else {
                if (bLock) {
                    const top = this.top + this.height;
                    await this.afterPanel.draggingMove(top);
                } else if (aLock) {
                    const num = this.getAfterMinHeight();
                    const top = this.containerHeight - (num + this.height);
                    await this.draggingMove(top);
                } else {
                    // 计算可以处与的最高和最低位置
                    const minTop = this.getBeforeMinHeight();
                    const maxTop = this.containerHeight - (this.getAfterMinHeight() + this.minHeight);
                    // 默认从上边往下算
                    let top = this.beforePanel.top + this.beforePanel.height - this.height + oldMinHeight;
                    if (top < minTop) {
                        top = minTop;
                    } else if (top > maxTop) {
                        top = this.afterPanel.top - this.height;
                    }
                    await this.draggingMove(top);
                }
                if (aLock) {
                    const num = this.getAfterMinHeight();
                    await this.afterPanel.draggingMove(this.containerHeight - num);
                    this.height = this.afterPanel.top - this.top;
                }
            }
            this.panels.forEach(item => item.calcIsShowDragTrigger());
            this.container.tick();
            this.disableTransition();
        }
    }

    /**
     * 计算拖拽节点是否可拖动
     *
     * @memberof IBzSplitViewPanel
     */
    @Method()
    async calcIsShowDragTrigger() {
        if (
            (this.afterAllLockHeight() && !this.isLast) ||
            (this.beforeAllLockHeight() && !this.isFirst) ||
            (this.isLast && this.isLockHeight)
        ) {
            this.dragTrigger?.hiddenSelf();
        } else {
            this.dragTrigger?.showSelf();
        }
    }

    /**
     * 之前所有项高度是否锁定
     *
     * @return {*}  {boolean}
     * @memberof IBzSplitViewPanel
     */
    beforeAllLockHeight(): boolean {
        for (let i = 0; i < this.index; i++) {
            if (!this.panels[i].isLockHeight) {
                return false;
            }
        }
        return true;
    }

    /**
     * 之前包含自己以及以前全部锁定
     *
     * @readonly
     * @type {boolean}
     * @memberof IBzSplitViewPanel
     */
    beforeAllLockHeightAndSelf(): boolean {
        return this.isLockHeight && this.beforeAllLockHeight();
    }

    /**
     * 之后所有项高度是否锁定
     *
     * @return {*}  {boolean}
     * @memberof IBzSplitViewPanel
     */
    afterAllLockHeight(): boolean {
        for (let i = this.index + 1; i < this.panels.length; i++) {
            if (!this.panels[i].isLockHeight) {
                return false;
            }
        }
        return true;
    }

    /**
     * 之后包含自己全部锁定
     *
     * @readonly
     * @type {boolean}
     * @memberof IBzSplitViewPanel
     */
    afterAllLockHeightAndSelf(): boolean {
        return this.isLockHeight && this.afterAllLockHeight();
    }

    /**
     * 获取前置组件最小高度合「包含自己」
     *
     * @return {*}  {number}
     * @memberof IBzSplitViewPanel
     */
    getBeforeMinHeight(): number {
        let num = 0;
        for (let i = 0; i < this.index; i++) {
            num += this.panels[i].minHeight;
        }
        return num;
    }

    /**
     * 获取后置组件最小高度合「包含自己」
     *
     * @return {*}  {number}
     * @memberof IBzSplitViewPanel
     */
    getAfterMinHeight(): number {
        let num = 0;
        for (let i = this.index + 1; i < this.panels.length; i++) {
            num += this.panels[i].minHeight;
        }
        return num;
    }
}
